﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry
{
    public static class Tangent
    {
        /// <summary>
        /// Tangent of a circle, having certain angle
        /// </summary>
        /// <param name="c"></param>
        /// <param name="a"></param>
        /// <returns>Tangent Line</returns>
        public static Line CircleAngle(Circle c, Angle a)
        {
            Line connecting = new Line(c.center, a);
            connecting = connecting.Parallel(c.radius, true);
            if (Tangent.SameDirections(c, connecting))
            {
                return connecting;
            }
            else
            {
                connecting = connecting.Parallel(c.radius, false);
                return connecting;
            }
        }

        /// <summary>
        /// Tangent of a circle, crossing certain point
        /// </summary>
        /// <param name="c"></param>
        /// <param name="p">Point on the Line</param>
        /// <returns>Tangent Line</returns>
        public static Line CirclePoint(Circle c, Point p)
        {
            Line connecting = new Line(p, c.center);
            double r = p.Distance(c.center);
            Angle beta = new Angle(Math.Sin(c.radius / r));
            Angle alpha;
            alpha = new Angle(connecting.vec.angle - beta);
            Line ret = new Line(p, alpha);
            if (Tangent.SameDirections(c, connecting)) return ret;
            alpha = new Angle(connecting.vec.angle + beta);
            return ret = new Line(p, alpha);
        }

        /// <summary>
        /// Tangent of 2 circles
        /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <returns></returns>
        public static Line CircleCircle(Circle c1, Circle c2)
        {
            //make c1 smaller circle
            if (c1.radius > c2.radius)
            {
                Circle tmp = new Circle(c1);
                c1 = c2;
                c2 = tmp;
            }

            double r = c1.radius;
            double R = c2.radius;
            double dist = c1.center.Distance(c1.center);
            Line l = new Line(c2.center, c1.center);

            if ((c1.clockwise == true && c2.clockwise == true) || (c1.clockwise == false && c2.clockwise == false))
            {
                double a = r * dist / (R - r);
                l.vec.start_point = c1.center;
                l.vec.SetLength(a);
                Point p = new Point(c1.center);
                p.Move(l.vec);
                l = Tangent.CirclePoint(c1, p);
            }
            else
            {
                double a = r * dist / (R + r);
                l.vec.Rotate(new Angle(Math.PI));
                l.vec.start_point = c1.center;
                l.vec.SetLength(dist);
                Point p = new Point(l.vec.EndPoint());
                l = Tangent.CirclePoint(c1, p);
            }
            return l;
        }

        /// <summary>
        /// Tangent of circle with the angle 'angle' from the line l
        /// </summary>
        /// <param name="c"></param>
        /// <param name="angle">Angle between the line l and tangent</param>
        /// <param name="l"></param>
        /// <returns></returns>
        public static Line CircleAnglefromLine(Circle c, Angle a, Line l)
        {
            Angle an = a + l.vec.angle;
            return Tangent.CircleAngle(c, an);
        }

        /// <summary>
        /// Circle Tangent to 2 lines
        /// </summary>
        /// <param name="l1"></param>
        /// <param name="l2"></param>
        /// <returns></returns>
        public static Circle LineLine(Line l1, Line l2, double radius, bool circ_clockwise = true)
        {
            Point lineinter = Intersection.GetIntersectionPoint(l1, l2);
            l1.vec.start_point = lineinter;
            l2.vec.start_point = lineinter;

            //Setting circle_center_vector to be on the line between vectors
            Vector circle_center_vector = new Vector(l1.vec);
            circle_center_vector = l1.vec + l2.vec;
            circle_center_vector.Rotate(new Angle(Math.PI));
            circle_center_vector.start_point = lineinter;

            //Checking if the circle is on the right side of the lines intersection point
            if (l1.vec.CrossProduct(circle_center_vector) >= 0 && circ_clockwise == true) 
                circle_center_vector.ChangeDirection();
            if(l1.vec.CrossProduct(circle_center_vector) <= 0 && circ_clockwise == false)
                circle_center_vector.ChangeDirection();

            //calculating distance between lines intersection point and middle of the tangent circle
            Angle between1 = circle_center_vector.AngleBetween(l1.vec);
            Angle between2 = circle_center_vector.AngleBetween(l2.vec);
            if (between1 > between2) between1 = between2;

            double distance = radius / between1.Sin();

            //setting the middle of the tangent circle
            circle_center_vector.SetLength(distance);
            Point circle_center = circle_center_vector.EndPoint();

            return new Circle(circle_center, radius, circ_clockwise);            
        }

        /// <summary>
        /// Circle Tangent to line and another circle
        /// </summary>
        /// <param name="l"></param>
        /// <param name="c"></param>
        /// <returns></returns>
        public static Circle LineCircle(Line l, Circle c)
        {
            return new Circle();
        }

        /// <summary>
        /// Circle Tangent to line with radiant R, having point p on the perimeter
        /// </summary>
        /// <param name="r"></param>
        /// <param name="p"></param>
        /// <param name="l"></param>
        /// <returns></returns>
        public static Circle RadiantPointLine(float r, Point p, Line l)
        {
            return new Circle();
        }

        /// <summary>
        /// Circle Tangent to line with center in the point 'center'
        /// </summary>
        /// <param name="center"></param>
        /// <param name="l"></param>
        /// <returns></returns>
        public static Circle CenterLine(Point center, Line l)
        {
            return new Circle();
        }

        /// <summary>
        /// Circle Tangent to 2 circles
        /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <returns></returns>
        public static Circle CircleCircle_(Circle c1, Circle c2)
        {
            return new Circle();
        }

        /// <summary>
        /// Check if the tangent_line to the circle c1 has same direction as a circle direction
        /// </summary>
        /// <param name="c1">Circle</param>
        /// <param name="tangent_line">Line tangent to the circle</param>
        /// <returns>True if same directions</returns>
        private static bool SameDirections(Circle c1, Line tangent_line)
        {
            if (tangent_line.OnLeft(c1.center))
            {
                if (c1.clockwise) return false;
                else return true;
            }

            else
            {
                if (c1.clockwise) return true;
                else return false;
            }
        }
    }
}
